#include "CBL_Locks.h"
#include "CBL_Debug.h"

CB_SUBNAMESPACE_START(Cpu)

#ifdef CB_WINDOWS

// ---------------------------------------------------------------------------------------------
// Windows Mutex implementation
// ---------------------------------------------------------------------------------------------

CMutex::CMutex(CMutex::EMutexType CB_UNUSED(_Type))
{
	InitializeCriticalSection(&m_Mutex);
}

// ------------------------------------------------------------------------------------------

CMutex::~CMutex()
{
	DeleteCriticalSection(&m_Mutex);
}

// ------------------------------------------------------------------------------------------

bool CMutex::TryLock()
{
	return TryEnterCriticalSection(&m_Mutex) != 0;
}

// ------------------------------------------------------------------------------------------

void CMutex::Lock()
{
	EnterCriticalSection(&m_Mutex);
}

// ------------------------------------------------------------------------------------------

bool CMutex::Unlock()
{
	LeaveCriticalSection(&m_Mutex);
	return true;
}

// ---------------------------------------------------------------------------------------------
// Windows RWLock implementation
// ---------------------------------------------------------------------------------------------

CRWLock::CRWLock()
: m_LockType(None)
{
	InitializeSRWLock(m_pLock);
}

// ------------------------------------------------------------------------------------------

CRWLock::~CRWLock()
{
}

// ------------------------------------------------------------------------------------------

void CRWLock::ReadLock()
{
	if (Debug::Verify(m_LockType != Exclusive, "Read Lock failed."))
	{
		m_LockType = Shared;
		AcquireSRWLockShared(m_pLock);
	}
}

// ------------------------------------------------------------------------------------------

bool CRWLock::TryReadLock()
{
	if (m_LockType == None)
	{	
		ReadLock();
		return true;
	}

	return false;
}

// ------------------------------------------------------------------------------------------

void CRWLock::WriteLock()
{
	if (Debug::Verify(m_LockType != Shared, "Write Lock failed."))
	{
		m_LockType = Exclusive;
		AcquireSRWLockExclusive(m_pLock);
	}
}

// ------------------------------------------------------------------------------------------

bool CRWLock::TryWriteLock()
{
	if (m_LockType == None)
	{	
		WriteLock();
		return true;
	}

	return false;
}

// ------------------------------------------------------------------------------------------

bool CRWLock::Unlock()
{
	switch (m_LockType)
	{
	case Shared:
		ReleaseSRWLockShared(m_pLock);
		m_LockType = None;
		return true;

	case Exclusive:
		ReleaseSRWLockExclusive(m_pLock);
		m_LockType = None;
		return true;

	default:
		break;
	};

	return false;
}

// ------------------------------------------------------------------------------------------

void UpgradeLock()
{
	ReleaseSRWLockShared(m_pLock);
	AcquireSRWLockExclusive(m_pLock);
}

// ------------------------------------------------------------------------------------------

bool TryUpgradeLock()
{
	ReleaseSRWLockShared(m_pLock);
	AcquireSRWLockExclusive(m_pLock);
	
	return true;
}

// ---------------------------------------------------------------------------------------------
// Windows Condition variable implementation
// ---------------------------------------------------------------------------------------------

CCondition::CCondition()
{
	InitializeConditionVariable(&m_Condition);
	InitializeCriticalSection(&m_Mutex);
	EnterCriticalSection(&m_Mutex);
}

// ------------------------------------------------------------------------------------------

CCondition::~CCondition()
{
	DeleteCriticalSection(&m_Mutex);
}

// ------------------------------------------------------------------------------------------

void CCondition::Wait()
{
	if (TryEnterCriticalSection(&m_Mutex))
    	LeaveCriticalSection(&m_Mutex); // we are already paused
    else
	{
    	SleepConditionVariableCS(&m_Condition, &m_Mutex, INFINITE);
		LeaveCriticalSection(&m_Mutex);
	}
}

// ------------------------------------------------------------------------------------------

void CCondition::Signal()
{
	WakeConditionVariable(&m_Condition);
	TryEnterCriticalSection(&m_Mutex);
}

// ------------------------------------------------------------------------------------------

void CCondition::Broadcast()
{
	WakeAllConditionVariable(&m_Condition);
	TryEnterCriticalSection(&m_Mutex);
}

#else

// ---------------------------------------------------------------------------------------------
// POSIX Mutex implementation
// ---------------------------------------------------------------------------------------------

CMutex::CMutex(CMutex::EMutexType _Type)
{
	static int NativeType[NumLockTypes] = 
    {
    	PTHREAD_MUTEX_RECURSIVE,
        PTHREAD_MUTEX_ERRORCHECK,
        PTHREAD_MUTEX_NORMAL,
    };
    
	pthread_mutexattr_t Attributes;
    pthread_mutexattr_init(&Attributes);
    pthread_mutexattr_settype(&Attributes, NativeType[_Type]);
    
	Debug::Verify(pthread_mutex_init(&m_MutexId, &Attributes) == 0, "Mutex construction failed.");
}

// ------------------------------------------------------------------------------------------

CMutex::~CMutex()
{
	Debug::Verify(pthread_mutex_destroy(&m_MutexId) == 0, "Mutex destrution failed.");
}

// ------------------------------------------------------------------------------------------

bool CMutex::TryLock()
{
	return (pthread_mutex_trylock(&m_MutexId) == 0);
}

// ------------------------------------------------------------------------------------------

void CMutex::Lock()
{
	Debug::Verify(pthread_mutex_lock(&m_MutexId) == 0, "Mutex lock failed.");
}

// ------------------------------------------------------------------------------------------

bool CMutex::Unlock()
{
	return (pthread_mutex_unlock(&m_MutexId) == 0);
}

// ---------------------------------------------------------------------------------------------
// POSIX RWLock implementation
// ---------------------------------------------------------------------------------------------

CRWLock::CRWLock()
{
	Debug::Verify(pthread_rwlock_init(&m_LockId, NULL) == 0, "Read/Write lock construction failed.");
}

// ------------------------------------------------------------------------------------------

CRWLock::~CRWLock()
{
	Debug::Verify(pthread_rwlock_destroy(&m_LockId) == 0, "Read/Write lock destrution failed.");
}

// ------------------------------------------------------------------------------------------

void CRWLock::ReadLock()
{
	Debug::Verify(pthread_rwlock_rdlock(&m_LockId) == 0, "Read lock failed.");
}

// ------------------------------------------------------------------------------------------

bool CRWLock::TryReadLock()
{
	return (pthread_rwlock_tryrdlock(&m_LockId) == 0);
}

// ------------------------------------------------------------------------------------------

void CRWLock::WriteLock()
{
	Debug::Verify(pthread_rwlock_wrlock(&m_LockId) == 0, "Write lock failed.");
}

// ------------------------------------------------------------------------------------------

bool CRWLock::TryWriteLock()
{
	return (pthread_rwlock_trywrlock(&m_LockId) == 0);
}

// ------------------------------------------------------------------------------------------

void CRWLock::UpgradeLock()
{
	pthread_rwlock_unlock(&m_LockId);
	Debug::Verify(pthread_rwlock_wrlock(&m_LockId) == 0, "Write lock failed.");
}

// ------------------------------------------------------------------------------------------

bool CRWLock::TryUpgradeLock()
{
	pthread_rwlock_unlock(&m_LockId);
	bool Result = (pthread_rwlock_trywrlock(&m_LockId) == 0);
	if (!Result)
		pthread_rwlock_rdlock(&m_LockId);
		
	return Result;
}

// ------------------------------------------------------------------------------------------

bool CRWLock::Unlock()
{
	return (pthread_rwlock_unlock(&m_LockId) == 0);
}

// ---------------------------------------------------------------------------------------------
// POSIX Condition variable implementation
// ---------------------------------------------------------------------------------------------

CCondition::CCondition()
{
	Debug::Verify(pthread_mutex_init(&m_MutexId, NULL),    "Condition Mutex construction failed.");
	Debug::Verify(pthread_cond_init(&m_ConditionId, NULL), "Condition construction failed.");
    
    pthread_mutex_lock(&m_MutexId);
}

// ------------------------------------------------------------------------------------------

CCondition::~CCondition()
{
	Debug::Verify(pthread_cond_destroy(&m_ConditionId), "Condition destrution failed.");
    Debug::Verify(pthread_mutex_destroy(&m_MutexId),    "Mutex destrution failed.");
}

// ------------------------------------------------------------------------------------------

void CCondition::Wait()
{
	if (pthread_mutex_trylock(&m_MutexId) == 0)
    	pthread_mutex_unlock(&m_MutexId); // we are already paused
    else
    	pthread_cond_wait(&m_ConditionId, &m_MutexId);
}

// ------------------------------------------------------------------------------------------

void CCondition::Signal()
{
	pthread_cond_signal(&m_ConditionId);
}

// ------------------------------------------------------------------------------------------

void CCondition::Broadcast()
{
	pthread_cond_broadcast(&m_ConditionId);
}

#endif

CB_SUBNAMESPACE_END